'use strict';

const log = require('console').log;
// const shell = require('shelljs');

const fs = require('fs');
const path = require('path');
const md5 = require('md5');
const mkdirp = require('mkdirp');

const Digger = require('./digger.js');
const Parser = require('./parser.js');
const CssDigger = require('./cssDigger.js');
const CssParser = require('./cssParser.js');
const Getter = require('./getter.js');

const cfg = require('../configs/config.json');

class WebpageGetter {

    constructor(options = {}) {
        this.log = log;
        this.cfg = cfg;
        this.dest = "";
        Object.assign(this.cfg, options);
        this.output = this.cfg.output || '';

        this.digger = new Digger(this.cfg.html);
        this.parser = new Parser(this.cfg.html);
        this.cssDigger = new CssDigger(this.cfg.css);
        this.cssParser = new CssParser(this.cfg.css);
        this.getter = new Getter(this.cfg);
    }

    async fetch(url, forceFetch = false) {
        const dest = md5(url);

        console.log('dest:', dest);

        return new Promise(async (resolve, reject) => {
            //check cache
            if (!forceFetch
                && fs.existsSync(path.join(this.output, dest + ".html"))
                && fs.existsSync(path.join(this.output, dest))
            ) {
                resolve(dest);
                return;
            }

            console.log('fetch ', url);

            try {
                const content = await this.getContent(url);
                await this.saveAs(url, content, dest);
                resolve(dest);
            } catch (error) {
                reject(error);
            }
        });

    }

    genIdentifier() {
        const d = new Date();
        const ifNeedZero = (number) => {
            return number < 10 ? "0" + number.toString() : number.toString();
        }

        return d.getFullYear().toString() + ifNeedZero(d.getMonth() + 1) +
            ifNeedZero(d.getUTCDate()) + ifNeedZero(d.getHours().toString()) +
            ifNeedZero(d.getMinutes().toString()) + ifNeedZero(d.getSeconds().toString());
    }

    async getContent(url) {
        return new Promise((resolve, reject) => {
            this.getter.splashHtml(url).then(content => {
                resolve(content);
            }).catch(error => {
                reject(error);
            })
        })
    }

    async saveAs(url, content, dest) {
        const { digger, parser, cssDigger, cssParser, getter } = this;

        return new Promise((resolve, reject) => {

            let that = this;

            this.dest = dest;

            mkdirp.sync(path.join(this.output, this.dest))

            digger.dig(content);

            const originalAddrList = digger.originalAddrList();

            parser.parse(dest, this._getDomain(url), originalAddrList);

            const done = getter.downloadAllOfThese(this.cfg.verbose, 'FROM HTML: ', parser.downloadAddrList(), parser.saveAddrList(), this.output);

            this._makeHtmlFile(url, content, originalAddrList);

            done.then(() => {
                const fileList = fs.readdirSync(path.join(that.output, that.dest));
                if (fileList.length === 0) {
                    resolve(dest);
                    return;
                }

                fileList.map(function (file) {
                    if (file.substr(file.lastIndexOf('.')) === '.css') {
                        let content = fs.readFileSync(path.join(that.output, that.dest, file)).toString();

                        cssDigger.dig(content);
                        const originalAddrList = cssDigger.originalAddrList();

                        cssParser.parse(that.dest, path.join(that.dest, file), parser.cssBox(), originalAddrList);

                        if (cssParser.downloadAddrList().length > 0) {
                            const cssDone = getter.downloadAllOfThese(that.cfg.verbose, 'FROM CSS: ',
                                cssParser.downloadAddrList(), cssParser.saveAddrList(), that.output);
                            that._makeCssFile(file, content, originalAddrList, cssParser.replaceAddrList());
                            cssDone.then(() => {
                                resolve(dest);
                            })
                        }
                    } else {
                        resolve(dest);
                    }
                })
            })
        })
    }

    _getDomain(url) {
        let domain = '';

        if (url.substr(0, 7) != 'http://' && url.substr(0, 8) != 'https://') {
            throw new Error();
        }
        url = url.substr(url.length - 1) === '/' ? url : url + '/';

        if (url.substr(0, 7) === 'http://') {
            url = url.substr(7);
            domain = 'http://' + url.substr(0, url.indexOf('/'));
        } else {
            url = url.substr(8);
            domain = 'https://' + url.substr(0, url.indexOf('/'));
        }

        return domain;
    }

    _replace(content, originalAddrList, replaceAddrList, isCss) {
        originalAddrList.forEach((addr, idx) => {
            addr = addr.replace(new RegExp('\\?', 'g'), '\\?');
            content = content.replace(new RegExp(addr, 'g'), (isCss ? "../" : "") + replaceAddrList[idx]);
        });

        return content;
    }

    _makeFile(name, content) {
        const stream = fs.createWriteStream(path.join(this.output, name));
        stream.write(content);
        stream.end();
    }

    _makeHtmlFile(url, content, originalAddrList) {
        if (this.cfg.killScript) {
            content = content.replace(new RegExp('<script.*?</script>', 'g'), '');
            content = content.replace(new RegExp('<script.*?/>', 'g'), '');
            content = content.replace(new RegExp('<script', 'g'), '<textarea style="display:none" ');
            content = content.replace(new RegExp('</script>', 'g'), '</textarea>');
        }

        content = this._replace(content, originalAddrList, this.parser.replaceAddrList());
        this._makeFile(this.dest + '.html', '<!-- Current URL: ' + url + '-->\r\n' + content);
    }

    _makeCssFile(name, content, originalArr, replaceArr) {
        fs.renameSync(path.join(this.output, this.dest, name), path.join(this.output, this.dest, name + '.xss'));

        content = this._replace(content, originalArr, replaceArr, true);
        this._makeFile(path.join(this.dest, name), content);
    }
}

module.exports = WebpageGetter;